package org.fhawk.tinyspring.orm;

import org.apache.commons.dbutils.BasicRowProcessor;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.RowProcessor;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.fhawk.tinyspring.orm.annotation.ID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by tuo on 10/8/16.
 */
@SuppressWarnings("unchecked")
public class SimpleMapperImpl<T> implements Mapper<T> {
    private Logger logger = LoggerFactory.getLogger(SimpleMapperImpl.class);
    private static final QueryRunner run = new QueryRunner();
    private Class<T> clazz = getClazz();
    private RowProcessor processor = new BasicRowProcessor(new CustomBeanProcessor());
    private ResultSetHandler<T> handler = new BeanHandler<>(clazz, processor);
    private ResultSetHandler<List<T>> listHandler = new BeanListHandler<>(clazz, processor);


    @Override
    public int update(T t) throws SQLException {
        StringBuilder sql = new StringBuilder();
        String tableName = clazz.getSimpleName().toLowerCase();
        Field[] fields = clazz.getDeclaredFields();
        List<Object> params = new ArrayList<>();
        //判断ID是否有值，用以确定添加还是修改
        List<Field> fieldList = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(ID.class) && Objects.nonNull(getFieldValue(field, t)) && !Objects.equals(getFieldValue(field, t), 0)).collect(Collectors.toList());
        if (!fieldList.isEmpty()) {//ID有值 更新
            sql.append("UPDATE ").append(tableName).append(" SET ");
            Arrays.stream(fields)
                .filter(field -> Objects.nonNull(getFieldValue(field, t)))
                .forEach(field -> {
                    sql.append(field.getName()).append("=").append("?").append(",");
                    params.add(getFieldValue(field, t));
                });
            //删除最后一个逗号
            sql.deleteCharAt(sql.length() - 1);
            //添加条件
            Field field = fieldList.get(0);
            sql.append(" WHERE ").append(field.getName()).append("=").append(getFieldValue(field, t));
        } else {//添加一条新的记录
            fieldList = Arrays.stream(fields).filter(field -> Objects.nonNull(getFieldValue(field, t)) && !Objects.equals(getFieldValue(field, t), 0)).collect(Collectors.toList());
            sql.append("INSERT INTO ").append(tableName).append("( ");
            for (Field field : fieldList) {
                sql.append(field.getName()).append(",");
            }
            sql.deleteCharAt(sql.length() - 1);
            sql.append(") VALUES(");
            for (Field field : fieldList) {
                sql.append("?").append(",");
                params.add(getFieldValue(field, t));
            }
            sql.deleteCharAt(sql.length() - 1);
            sql.append(")");
        }
        try {
            return run.update(ConnectionFactory.getConnection(), sql.toString(), params.toArray());
        } finally {
            ConnectionFactory.close();
        }
    }


    @Override
    public int execute(String sql, Object... params) throws SQLException {
        try {
            return run.execute(ConnectionFactory.getConnection(), sql, params);
        } finally {
            ConnectionFactory.close();
        }
    }

    @Override
    public int delete(Serializable id) throws SQLException {
        Field[] fields = clazz.getDeclaredFields();
        List<Field> fieldList = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(ID.class)).collect(Collectors.toList());
        try {
            return run.update(ConnectionFactory.getConnection(), "DELETE FROM " + clazz.getSimpleName() + " WHERE " + fieldList.get(0).getName() + "=" + id);
        } finally {
            ConnectionFactory.close();
        }
    }

    @Override
    public T get(Serializable id) throws SQLException {
        List<Field> fieldList = Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(ID.class)).collect(Collectors.toList());
        String sql = "SELECT * FROM " + clazz.getSimpleName() + " WHERE " + fieldList.get(0).getName() + "=" + id;
        try {
            return run.query(ConnectionFactory.getConnection(), sql, handler);
        } finally {
            ConnectionFactory.close();
        }
    }

    @Override
    public List<T> find(T t) throws SQLException {
        StringBuilder sql = new StringBuilder("select * from " + clazz.getSimpleName() + " where 1=1 ");
        List<Field> fields = Arrays.stream(clazz.getDeclaredFields()).filter(field -> Objects.nonNull(getFieldValue(field, t)) && !Objects.equals(getFieldValue(field, t), 0)).collect(Collectors.toList());
        for (Field field : fields) {
            sql.append(" AND ").append(field.getName()).append("=").append(getFieldValue(field, t));
        }
        try {
            return run.query(ConnectionFactory.getConnection(), sql.toString(), listHandler);
        } finally {
            ConnectionFactory.close();
        }
    }

    @Override
    public List<T> findAll() throws SQLException {
        try {
            return run.query(ConnectionFactory.getConnection(), "select * from " + clazz.getSimpleName(), listHandler);
        } finally {
            ConnectionFactory.close();
        }
    }

    @Override
    public Map<String, Object> findToMap(String sql, Object... params) throws SQLException {
        try {
            return run.query(ConnectionFactory.getConnection(), sql, new MapHandler(), params);
        } finally {
            ConnectionFactory.close();
        }
    }

    @Override
    public List<Map<String, Object>> findToList(String sql, Object... params) throws SQLException {
        try {
            return run.query(ConnectionFactory.getConnection(), sql, new MapListHandler(), params);
        } finally {
            ConnectionFactory.close();
        }
    }


    /**
     * 获取子类泛型的实际类
     *
     * @return 泛型类
     */
    private Class<T> getClazz() {
        Type type = getClass().getGenericSuperclass();
        Type[] types = ((ParameterizedType) type).getActualTypeArguments();
        if (types.length == 1) {
            return (Class<T>) types[0];
        }
        return null;
    }

    /**
     * 获取指定对象中指定属性的值
     *
     * @param field 属性
     * @param t     对象
     * @return 值
     */
    private Object getFieldValue(Field field, T t) {
        try {
            field.setAccessible(true);
            return field.get(t);
        } catch (IllegalAccessException e) {
            logger.error("获取指定对象{}的属性值{}失败", clazz.getSimpleName(), field.getName(), e);
        }
        return null;
    }
}
